#include "command.h"
#include "graph.priv.h"
#include "listmodel.h"
#include "misc.h"
#include <array>

class CommandChangeType: public Command {
	int16_t type;
	std::vector<std::pair<Edge, int16_t>> oldtypes;
	void getChildren(Edge e, std::vector<bool>& marked) {
		if(marked[e.index()]) return;
		oldtypes.emplace_back(e, e.type());
		marked[e.index()]=true;
		for(auto& p: e.leftVertex().neighbors()) {
			if(p.first.parentEdge()==e)
				getChildren(p.first, marked);
		}
		for(auto& p: e.rightVertex().neighbors()) {
			if(p.first.parentEdge()==e)
				getChildren(p.first, marked);
		}
	}
	void getNeighbors(Edge e, std::vector<bool>& marked) {
		if(marked[e.index()]) return;
		oldtypes.emplace_back(e, e.type());
		marked[e.index()]=true;
		for(auto& p: e.leftVertex().neighbors()) {
			getNeighbors(p.first, marked);
		}
		for(auto& p: e.rightVertex().neighbors()) {
			getNeighbors(p.first, marked);
		}
	}
	public:
	CommandChangeType(SessionPriv* tp, Edge e, int16_t t):
		Command{tp, "Change neurite type"}, type{t}, oldtypes{}
	{
		std::vector<bool> marked(graph().edges().size());
		if(e.tree()) {
			auto pe=e.parentEdge();
			while(pe) { e=pe; pe=e.parentEdge(); }
			getChildren(e, marked);
		} else {
			getNeighbors(e, marked);
		}
	}
	void undoImp(SessionPriv* tp) override {
		for(auto& p: oldtypes) {
			auto ep=EdgePriv::get(p.first);
			ep->type=p.second;
		}
	}
	void redoImp(SessionPriv* tp) override {
		for(auto& p: oldtypes) {
			auto ep=EdgePriv::get(p.first);
			ep->type=type;
		}
	}
};

class CommandChangeMark: public Command {
	int16_t mark;
	int16_t oldmark;
	Edge edg;
	size_t edgPos;
	public:
	CommandChangeMark(SessionPriv* tp, Edge e, size_t ep, int16_t m):
		Command{tp, "Change node mark"}, mark{m}, edg{e}, edgPos{ep}
	{
		oldmark=edg.points()[edgPos].m;
	}
	void undoImp(SessionPriv* tp) override {
		auto ep=EdgePriv::get(edg);
		ep->points[edgPos].m=oldmark;
		updateBuffer(ep, edgPos);
	}
	void redoImp(SessionPriv* tp) override {
		auto ep=EdgePriv::get(edg);
		ep->points[edgPos].m=mark;
		updateBuffer(ep, edgPos);
	}
};

class CommandChangeSize: public Command {
	uint16_t size;
	std::vector<std::tuple<Edge, size_t, uint16_t>> oldsizes;
	void getNeighbors(Vertex v) {
		for(auto& p: v.neighbors()) {
			size_t ep=p.second?p.first.points().size()-1:0;
			oldsizes.emplace_back(p.first, ep, p.first.points()[ep]._r);
		}
	}
	public:
	CommandChangeSize(SessionPriv* tp, Edge e, size_t ep, uint16_t m):
		Command{tp, "Change node size"}, size{m}, oldsizes{}
	{
		if(ep==0) {
			getNeighbors(e.leftVertex());
		} else if(ep==e.points().size()-1) {
			getNeighbors(e.rightVertex());
		} else {
			oldsizes.emplace_back(e, ep, e.points()[ep]._r);
		}
	}
	void undoImp(SessionPriv* tp) override {
		for(auto& t: oldsizes) {
			auto ep=EdgePriv::get(std::get<0>(t));
			ep->points[std::get<1>(t)]._r=std::get<2>(t);
			updateBuffer(ep, std::get<1>(t));
		}
	}
	void redoImp(SessionPriv* tp) override {
		for(auto& t: oldsizes) {
			auto ep=EdgePriv::get(std::get<0>(t));
			ep->points[std::get<1>(t)]._r=size;
			updateBuffer(ep, std::get<1>(t));
		}
	}
};

class CommandNewNeuron: public Command {
	Tree nrn;
	public:
	CommandNewNeuron(SessionPriv* tp, Vertex v):
		Command{tp, "Create neuron"}, nrn{}
	{
		nrn=TreePriv::alloc();
		auto np=TreePriv::get(nrn);
		np->root=v;
		int i=0;
		auto name=QString{"Neuron %1"}.arg(i).toStdString();
		while(true) {
			bool hit=false;
			for(auto n: graph().trees()) {
				if(n.name()==name) {
					hit=true;
					break;
				}
			}
			if(!hit) break;
			i++;
			name=QString{"Neuron %1"}.arg(i).toStdString();
		}
		np->name=name;
		np->selected=false;
	}
	void undoImp(SessionPriv* tp) override {
		auto gp=GraphPriv::get(graph());
		model()->beginRemoveRow(nrn.index());
		gp->trees.erase(gp->trees.begin()+nrn.index());
		model()->endRemoveRow();
		gp->updateModel();
		for(auto n: gp->trees) {
			auto np=TreePriv::get(n);
			if(np->completed!=np->completed_prev)
				model()->emitDataChanged(np->index, 0, 0);
		}
	}
	void redoImp(SessionPriv* tp) override {
		auto gp=GraphPriv::get(graph());
		auto np=TreePriv::get(nrn);
		np->completed=false;
		np->selected=false;
		model()->beginInsertRow(gp->trees.size());
		gp->trees.push_back(nrn);
		model()->endInsertRow();
		gp->updateModel();
		for(auto n: gp->trees) {
			auto np=TreePriv::get(n);
			if(np->completed!=np->completed_prev)
				model()->emitDataChanged(np->index, 0, 0);
		}
	}
	~CommandNewNeuron() override {
		if(!performed()) {
			TreePriv::free(nrn);
		}
	}
};
class CommandRenameNeuron: public Command {
	Tree nrn;
	std::string name;
	std::string oldname;
	public:
	CommandRenameNeuron(SessionPriv* tp, Tree n, const std::string& nm):
		Command{tp, "Rename neuron"}, nrn{n}, name{nm}
	{
		oldname=nrn.name();
	}
	void undoImp(SessionPriv* tp) override {
		auto np=TreePriv::get(nrn);
		np->name=oldname;
		model()->emitDataChanged(np->index, 1, 1);
	}
	void redoImp(SessionPriv* tp) override {
		auto np=TreePriv::get(nrn);
		np->name=name;
		model()->emitDataChanged(np->index, 1, 1);
	}
};

class CommandRemoveNeurons: public Command {
	std::vector<std::pair<Tree, size_t>> nrns;
	public:
	CommandRemoveNeurons(SessionPriv* tp, const std::vector<size_t>& idxes):
		Command{tp, "Remove neurons"}, nrns{}
	{
		for(auto i: idxes) {
			nrns.emplace_back(graph().trees()[i], i);
		}
	}
	void undoImp(SessionPriv* tp) override {
		auto gp=GraphPriv::get(graph());
		for(auto& p: nrns) {
			auto np=TreePriv::get(p.first);
			np->completed=false;
			np->selected=false;
			model()->beginInsertRow(p.second);
			gp->trees.insert(gp->trees.begin()+p.second, p.first);
			model()->endInsertRow();
		}
		gp->updateModel();
		for(auto n: gp->trees) {
			auto np=TreePriv::get(n);
			if(np->completed!=np->completed_prev)
				model()->emitDataChanged(np->index, 0, 0);
		}
	}
	void redoImp(SessionPriv* tp) override {
		auto gp=GraphPriv::get(graph());
		size_t shift=0;
		for(auto& p: nrns) {
			model()->beginRemoveRow(p.second-shift);
			gp->trees.erase(gp->trees.begin()+p.second-shift);
			model()->endRemoveRow();
			shift++;
		}
		gp->updateModel();
		for(auto n: gp->trees) {
			auto np=TreePriv::get(n);
			if(np->completed!=np->completed_prev)
				model()->emitDataChanged(np->index, 0, 0);
		}
	}
	~CommandRemoveNeurons() override {
		if(performed()) {
			for(auto& p: nrns) {
				TreePriv::free(p.first);
			}
		}
	}
};

class CommandPurgeNeurons: public Command {
	std::vector<std::pair<Vertex, size_t>> oldvertices;
	std::vector<Vertex> vertices;
	std::vector<std::pair<Edge, size_t>> oldedges;
	std::vector<std::pair<Tree, size_t>> nrns;
	public:
	CommandPurgeNeurons(SessionPriv* tp, std::vector<size_t> idxes):
		Command{tp, "Purge neurons"}, oldvertices{}, vertices{},
		oldedges{}, nrns{}
	{
		std::vector<bool> nmark(graph().trees().size(), false);
		for(auto i: idxes) {
			nrns.emplace_back(graph().trees()[i], i);
			nmark[i]=true;
		}
		std::vector<bool> emark(graph().edges().size(), false);
		Vertex curVert{};
		Vertex tgtVert{};
		bool chgCur=false;
		bool chgTgt=false;
		for(auto e: graph().edges()) {
			if(e.tree() && nmark[e.tree().index()]) {
				oldedges.emplace_back(e, e.index());
				emark[e.index()]=true;

				if(e==curPos().edge) {
					if(curPos().index==0) {
						curVert=e.leftVertex();
					} else if(curPos().index==e.points().size()-1) {
						curVert=e.rightVertex();
					}
					chgCur=true;
				}
				if(e==tgtPos().edge) {
					if(tgtPos().index==0) {
						tgtVert=e.leftVertex();
					} else if(tgtPos().index==e.points().size()-1) {
						tgtVert=e.rightVertex();
					}
					chgTgt=true;
				}
			}
		}

		for(auto v: graph().vertices()) {
			size_t nrm=0;
			for(auto& p: v.neighbors()) {
				if(emark[p.first.index()]) {
					nrm++;
				}
			}
			if(nrm==0)
				continue;

			oldvertices.emplace_back(v, v.index());
			if(nrm==v.neighbors().size())
				continue;

			auto vert=VertexPriv::alloc();
			auto vp=VertexPriv::get(vert);
			for(auto& p: v.neighbors()) {
				if(!emark[p.first.index()]) {
					vp->neighbors.push_back(p);
				}
			}
			vp->finished=false;
			vertices.emplace_back(vert);

			auto& adj=vp->neighbors[0];
			auto adji=adj.second?adj.first.points().size()-1:0;
			if(chgCur && curVert==v) {
				setCurPos({adj.first, adji, Point{}});
				chgCur=false;
			}
			if(chgTgt && tgtVert==v) {
				setTgtPos({adj.first, adji, Point{}});
				chgTgt=false;
			}
		}
		if(chgCur) {
			setCurPos({Edge{}, 0, Point{}});
		}
		if(chgTgt) {
			setTgtPos({Edge{}, 0, Point{}});
		}
	}
	void undoImp(SessionPriv* tp) override {
		auto gp=GraphPriv::get(graph());
		size_t shift=0;
		for(auto& v: vertices) {
			gp->vertices.erase(gp->vertices.begin()+v.index()-shift);
			shift++;
		}
		for(auto& p: oldvertices) {
			gp->vertices.insert(gp->vertices.begin()+p.second, p.first);
			for(auto& pp: p.first.neighbors()) {
				auto ep=EdgePriv::get(pp.first);
				if(pp.second) {
					ep->rightVertex=p.first;
				} else {
					ep->leftVertex=p.first;
				}
			}
		}
		for(auto& p: oldedges) {
			gp->edges.insert(gp->edges.begin()+p.second, p.first);
		}
		for(auto& p: nrns) {
			auto np=TreePriv::get(p.first);
			np->completed=false;
			np->selected=false;
			model()->beginInsertRow(p.second);
			gp->trees.insert(gp->trees.begin()+p.second, p.first);
			model()->endInsertRow();
		}
		gp->updateModel();
		for(auto n: gp->trees) {
			auto np=TreePriv::get(n);
			if(np->completed!=np->completed_prev)
				model()->emitDataChanged(np->index, 0, 0);
		}
	}
	void redoImp(SessionPriv* tp) override {
		auto gp=GraphPriv::get(graph());
		size_t shift=0;
		for(auto& p: nrns) {
			model()->beginRemoveRow(p.second-shift);
			gp->trees.erase(gp->trees.begin()+p.second-shift);
			model()->endRemoveRow();
			shift++;
		}
		shift=0;
		for(auto& p: oldedges) {
			gp->edges.erase(gp->edges.begin()+p.second-shift);
			shift++;
		}
		shift=0;
		for(auto& p: oldvertices) {
			gp->vertices.erase(gp->vertices.begin()+p.second-shift);
			shift++;
		}
		for(auto v: vertices) {
			gp->vertices.push_back(v);
			for(auto& p: v.neighbors()) {
				auto ep=EdgePriv::get(p.first);
				if(p.second) {
					ep->rightVertex=v;
				} else {
					ep->leftVertex=v;
				}
			}
		}
		gp->updateModel();
		for(auto n: gp->trees) {
			auto np=TreePriv::get(n);
			if(np->completed!=np->completed_prev)
				model()->emitDataChanged(np->index, 0, 0);
		}
	}
	~CommandPurgeNeurons() override {
		if(performed()) {
			for(auto& p: oldvertices) {
				VertexPriv::free(p.first);
			}
			for(auto& p: oldedges) {
				auto ep=EdgePriv::get(p.first);
				freeBuffer(ep);
				EdgePriv::free(p.first);
			}
			for(auto& p: nrns) {
				TreePriv::free(p.first);
			}
		} else {
			for(auto& v: vertices) {
				VertexPriv::free(v);
			}
		}
	}
};

class CommandFinishBranch: public Command {
	std::vector<std::pair<Vertex, size_t>> oldvertices;
	std::vector<Vertex> vertices;
	std::vector<std::pair<Edge, size_t>> oldedges;
	std::vector<Edge> edges;
	std::vector<std::tuple<Tree, Vertex, Vertex>> soma;
	void sortPairs() {
		std::sort(oldvertices.begin(), oldvertices.end(), [](const std::pair<Vertex, size_t>& a, const std::pair<Vertex, size_t>& b) { return a.second<b.second; });
		std::sort(oldedges.begin(), oldedges.end(), [](const std::pair<Edge, size_t>& a, const std::pair<Edge, size_t>& b) { return a.second<b.second; });
	}
	void replaceVertex(Vertex v0, Vertex v1) {
		oldvertices.emplace_back(v0, v0.index());
		vertices.emplace_back(v1);
		if(v0.tree() && v0.tree().root()==v0) {
			soma.emplace_back(v0.tree(), v0, v1);
		}
	}
	bool tryMergeEdges(Vertex v) {
		if(v.neighbors().size()==2) {
			if(!v.tree() || v.tree().root()!=v) {
				auto e0=v.neighbors()[0].first;
				bool dir0=v.neighbors()[0].second;
				auto v0=dir0?e0.leftVertex():e0.rightVertex();
				auto e1=v.neighbors()[1].first;
				bool dir1=!v.neighbors()[1].second;
				auto v1=!dir1?e1.leftVertex():e1.rightVertex();
				if(v0==v1)
					return false;
				auto ne=EdgePriv::alloc();
				auto nep=EdgePriv::get(ne);
				nep->type=e0.type();
				nep->vaoi=-1;
				if(dir0) {
					for(size_t i=0; i<e0.points().size(); i++)
						nep->points.push_back(e0.points()[i]);
				} else {
					for(size_t i=e0.points().size(); i>0; i--)
						nep->points.push_back(e0.points()[i-1]);
				}
				if(v1==e1.rightVertex()) {
					for(size_t i=1; i<e1.points().size(); i++)
						nep->points.push_back(e1.points()[i]);
				} else {
					for(size_t i=e1.points().size(); i>1; i--)
						nep->points.push_back(e1.points()[i-2]);
				}
				updateBuffer(nep);
				oldedges.emplace_back(e0, e0.index());
				oldedges.emplace_back(e1, e1.index());
				edges.emplace_back(ne);
				mapEdge(e0, ne, dir0?0:(e0.points().size()-1), !dir0);
				mapEdge(e1, ne, dir1?(1-e0.points().size()):(e0.points().size()+e1.points().size()-2), !dir1);
				auto nv0=VertexPriv::alloc();
				auto nv0p=VertexPriv::get(nv0);
				nv0p->finished=v0.finished();
				for(auto& p: v0.neighbors()) {
					if(p.first==e0)
						nv0p->neighbors.push_back({ne, false});
					else
						nv0p->neighbors.push_back(p);
				}
				replaceVertex(v0, nv0);
				auto nv1=VertexPriv::alloc();
				auto nv1p=VertexPriv::get(nv1);
				nv1p->finished=v1.finished();
				for(auto& p: v1.neighbors()) {
					if(p.first==e1)
						nv1p->neighbors.push_back({ne, true});
					else
						nv1p->neighbors.push_back(p);
				}
				replaceVertex(v1, nv1);
				oldvertices.emplace_back(v, v.index());
				return true;
			}
		}
		return false;
	}
	void undoImpMerged(SessionPriv* tp) {
		auto gp=GraphPriv::get(graph());
		size_t shift=0;
		for(auto v: vertices) {
			gp->vertices.erase(gp->vertices.begin()+v.index()-shift);
			shift++;
		}
		for(auto& p: oldvertices) {
			gp->vertices.insert(gp->vertices.begin()+p.second, p.first);
			for(auto& pp: p.first.neighbors()) {
				auto ep=EdgePriv::get(pp.first);
				if(pp.second) {
					ep->rightVertex=p.first;
				} else {
					ep->leftVertex=p.first;
				}
			}
		}
		shift=0;
		for(auto e: edges) {
			gp->edges.erase(gp->edges.begin()+e.index()-shift);
			shift++;
		}
		for(auto& p: oldedges) {
			gp->edges.insert(gp->edges.begin()+p.second, p.first);
		}
		for(auto& t: soma) {
			auto np=TreePriv::get(std::get<0>(t));
			np->root=std::get<1>(t);
		}
		/*
		for(auto& p: oldneurons) {
			auto np=TreePriv::get(p.first);
			np->completed=false;
			np->selected=false;
			model()->beginInsertRow(p.second);
			gp->trees.insert(gp->trees.begin()+gp->trees.begin()+p.second, p.first);
			model()->endInsertRow();
		}
		*/
		gp->updateModel();
		for(auto n: gp->trees) {
			auto np=TreePriv::get(n);
			if(np->completed!=np->completed_prev)
				model()->emitDataChanged(np->index, 0, 0);
		}
	}
	void redoImpMerged(SessionPriv* tp) {
		auto gp=GraphPriv::get(graph());
		size_t shift=0;
		/*
		for(auto& p: oldneurons) {
			model()->beginRemoveRow(p.second-shift);
			gp->trees.erase(gp->trees.begin()+p.second-shift);
			model()->endRemoveRow();
			shift++;
		}
		*/
		for(auto& t: soma) {
			auto np=TreePriv::get(std::get<0>(t));
			np->root=std::get<2>(t);
		}
		shift=0;
		for(auto& p: oldedges) {
			gp->edges.erase(gp->edges.begin()+p.second-shift);
			shift++;
		}
		for(auto e: edges) {
			gp->edges.push_back(e);
		}
		shift=0;
		for(auto& p: oldvertices) {
			gp->vertices.erase(gp->vertices.begin()+p.second-shift);
			shift++;
		}
		for(auto v: vertices) {
			gp->vertices.push_back(v);
			for(auto& p: v.neighbors()) {
				auto ep=EdgePriv::get(p.first);
				if(p.second) {
					ep->rightVertex=v;
				} else {
					ep->leftVertex=v;
				}
			}
		}
		gp->updateModel();
		for(auto n: gp->trees) {
			auto np=TreePriv::get(n);
			if(np->completed!=np->completed_prev)
				model()->emitDataChanged(np->index, 0, 0);
		}
	}
	Vertex vert;
	bool merged;
	public:
	CommandFinishBranch(SessionPriv* tp, Vertex v, Edge e, size_t ei):
		Command{tp, "Finish branch"}, oldvertices{}, vertices{},
		oldedges{}, edges{}, soma{},
		vert{v}, merged{false}
	{
		if(e) {
			Position p{e, ei, Point{}};
			setCurPos(p);
		}
		merged=tryMergeEdges(v);
		if(merged) {
			sortPairs();
		}
	}
	void undoImp(SessionPriv* tp) override {
		if(merged)
			return undoImpMerged(tp);
		auto vp=VertexPriv::get(vert);
		vp->finished=false;
		auto gp=GraphPriv::get(graph());
		gp->updateModel();
		for(auto n: gp->trees) {
			auto np=TreePriv::get(n);
			if(np->completed!=np->completed_prev)
				model()->emitDataChanged(np->index, 0, 0);
		}
	}
	void redoImp(SessionPriv* tp) override {
		if(merged)
			return redoImpMerged(tp);
		auto vp=VertexPriv::get(vert);
		vp->finished=true;
		auto gp=GraphPriv::get(graph());
		gp->updateModel();
		for(auto n: gp->trees) {
			auto np=TreePriv::get(n);
			if(np->completed!=np->completed_prev)
				model()->emitDataChanged(np->index, 0, 0);
		}
	}
	~CommandFinishBranch() override {
		if(performed()) {
			for(auto& p: oldvertices) {
				VertexPriv::free(p.first);
			}
			for(auto& p: oldedges) {
				auto ep=EdgePriv::get(p.first);
				freeBuffer(ep);
				EdgePriv::free(p.first);
			}
			/*
			for(auto& p: oldneurons) {
				TreePriv::free(p.first);
			}
			*/
		} else {
			for(auto v: vertices) {
				VertexPriv::free(v);
			}
			for(auto e: edges) {
				auto ep=EdgePriv::get(e);
				freeBuffer(ep);
				EdgePriv::free(e);
			}
		}
	}
};

class CommandRemoveEdge: public Command {
	std::vector<std::pair<Vertex, size_t>> oldvertices;
	std::vector<Vertex> vertices;
	std::vector<std::pair<Edge, size_t>> oldedges;
	std::vector<Edge> edges;
	std::vector<std::tuple<Tree, Vertex, Vertex>> soma;
	std::vector<std::pair<Tree, size_t>> oldneurons;
	void replaceVertex(Vertex v0, Vertex v1) {
		oldvertices.emplace_back(v0, v0.index());
		vertices.emplace_back(v1);
		if(v0.tree() && v0.tree().root()==v0) {
			soma.emplace_back(v0.tree(), v0, v1);
		}
	}
	void removeVertex(Vertex v0) {
		oldvertices.emplace_back(v0, v0.index());
		if(v0.tree() && v0.tree().root()==v0) {
			oldneurons.emplace_back(v0.tree(), v0.tree().index());
		}
	}
	void addVertex(Vertex v0) {
		vertices.push_back(v0);
	}
	void sortPairs() {
		std::sort(oldvertices.begin(), oldvertices.end(), [](const std::pair<Vertex, size_t>& a, const std::pair<Vertex, size_t>& b) { return a.second<b.second; });
		std::sort(oldedges.begin(), oldedges.end(), [](const std::pair<Edge, size_t>& a, const std::pair<Edge, size_t>& b) { return a.second<b.second; });
		std::sort(oldneurons.begin(), oldneurons.end(), [](const std::pair<Tree, size_t>& a, const std::pair<Tree, size_t>& b) { return a.second<b.second; });
	}
	void undoImp(SessionPriv* tp) override {
		auto gp=GraphPriv::get(graph());
		size_t shift=0;
		for(auto v: vertices) {
			gp->vertices.erase(gp->vertices.begin()+v.index()-shift);
			shift++;
		}
		for(auto& p: oldvertices) {
			gp->vertices.insert(gp->vertices.begin()+p.second, p.first);
			for(auto& pp: p.first.neighbors()) {
				auto ep=EdgePriv::get(pp.first);
				if(pp.second) {
					ep->rightVertex=p.first;
				} else {
					ep->leftVertex=p.first;
				}
			}
		}
		shift=0;
		for(auto e: edges) {
			gp->edges.erase(gp->edges.begin()+e.index()-shift);
			shift++;
		}
		for(auto& p: oldedges) {
			gp->edges.insert(gp->edges.begin()+p.second, p.first);
		}
		for(auto& t: soma) {
			auto np=TreePriv::get(std::get<0>(t));
			np->root=std::get<1>(t);
		}
		for(auto& p: oldneurons) {
			auto np=TreePriv::get(p.first);
			np->completed=false;
			np->selected=false;
			model()->beginInsertRow(p.second);
			gp->trees.insert(gp->trees.begin()+p.second, p.first);
			model()->endInsertRow();
		}
		gp->updateModel();
		for(auto n: gp->trees) {
			auto np=TreePriv::get(n);
			if(np->completed!=np->completed_prev)
				model()->emitDataChanged(np->index, 0, 0);
		}
	}
	void redoImp(SessionPriv* tp) override {
		auto gp=GraphPriv::get(graph());
		size_t shift=0;
		for(auto& p: oldneurons) {
			model()->beginRemoveRow(p.second-shift);
			gp->trees.erase(gp->trees.begin()+p.second-shift);
			model()->endRemoveRow();
			shift++;
		}
		for(auto& t: soma) {
			auto np=TreePriv::get(std::get<0>(t));
			np->root=std::get<2>(t);
		}
		shift=0;
		for(auto& p: oldedges) {
			gp->edges.erase(gp->edges.begin()+p.second-shift);
			shift++;
		}
		for(auto e: edges) {
			gp->edges.push_back(e);
		}
		shift=0;
		for(auto& p: oldvertices) {
			gp->vertices.erase(gp->vertices.begin()+p.second-shift);
			shift++;
		}
		for(auto v: vertices) {
			gp->vertices.push_back(v);
			for(auto& p: v.neighbors()) {
				auto ep=EdgePriv::get(p.first);
				if(p.second) {
					ep->rightVertex=v;
				} else {
					ep->leftVertex=v;
				}
			}
		}
		gp->updateModel();
		for(auto n: gp->trees) {
			auto np=TreePriv::get(n);
			if(np->completed!=np->completed_prev)
				model()->emitDataChanged(np->index, 0, 0);
		}
	}
	void removeEdgeWhole(Edge e) {
		std::vector<std::pair<Edge, bool>> adjs0{};
		for(auto& p: e.leftVertex().neighbors()) {
			if(p.first!=e)
				adjs0.push_back(p);
		}
		if(adjs0.size()>0) {
			Vertex v0=VertexPriv::alloc();
			auto vp0=VertexPriv::get(v0);
			vp0->finished=false;
			vp0->neighbors=std::move(adjs0);
			replaceVertex(e.leftVertex(), v0);
			auto& adj=vp0->neighbors[0];
			auto adji=adj.second?adj.first.points().size()-1:0;
			if(curPos().edge==e && curPos().index==0) {
				setCurPos({adj.first, adji, Point{}});
			}
			if(tgtPos().edge==e && tgtPos().index==0) {
				setTgtPos({adj.first, adji, Point{}});
			}
		} else {
			removeVertex(e.leftVertex());
		}
		std::vector<std::pair<Edge, bool>> adjs1{};
		for(auto& p: e.rightVertex().neighbors()) {
			if(p.first!=e)
				adjs1.push_back(p);
		}
		if(adjs1.size()>0) {
			Vertex v1=VertexPriv::alloc();
			auto vp1=VertexPriv::get(v1);
			vp1->finished=false;
			vp1->neighbors=std::move(adjs1);
			replaceVertex(e.rightVertex(), v1);

			auto& adj=vp1->neighbors[0];
			auto adji=adj.second?adj.first.points().size()-1:0;
			if(curPos().edge==e && curPos().index==e.points().size()-1) {
				setCurPos({adj.first, adji, Point{}});
			}
			if(tgtPos().edge==e && tgtPos().index==e.points().size()-1) {
				setTgtPos({adj.first, adji, Point{}});
			}
		} else {
			removeVertex(e.rightVertex());
		}
		oldedges.emplace_back(e, e.index());
		if(curPos().edge==e)
			setCurPos({Edge{}, SIZE_MAX, Point{}});
		if(tgtPos().edge==e)
			setTgtPos({Edge{}, SIZE_MAX, Point{}});
	}
	void removeEdgeLeft(Edge e, size_t ei) {
		auto ne=EdgePriv::alloc();
		auto nep=EdgePriv::get(ne);
		nep->type=e.type();
		for(auto i=ei+1; i<e.points().size(); i++) {
			nep->points.push_back(e.points()[i]);
		}
		updateBuffer(nep);

		std::vector<std::pair<Edge, bool>> adjs0{};
		for(auto& p: e.leftVertex().neighbors()) {
			if(p.first!=e)
				adjs0.push_back(p);
		}
		if(adjs0.size()>0) {
			Vertex v0=VertexPriv::alloc();
			auto vp0=VertexPriv::get(v0);
			vp0->finished=false;
			vp0->neighbors=std::move(adjs0);
			replaceVertex(e.leftVertex(), v0);

			auto& adj=vp0->neighbors[0];
			auto adji=adj.second?adj.first.points().size()-1:0;
			if(curPos().edge==e && curPos().index==0) {
				setCurPos({adj.first, adji, Point{}});
			}
			if(tgtPos().edge==e && tgtPos().index==0) {
				setTgtPos({adj.first, adji, Point{}});
			}
		} else {
			removeVertex(e.leftVertex());
		}

		Vertex v1=VertexPriv::alloc();
		auto vp1=VertexPriv::get(v1);
		vp1->finished=e.rightVertex().finished();
		for(auto& p: e.rightVertex().neighbors()) {
			if(p.first==e)
				vp1->neighbors.push_back({ne, true});
			else
				vp1->neighbors.push_back(p);
		}
		replaceVertex(e.rightVertex(), v1);

		Vertex nv=VertexPriv::alloc();
		auto nvp=VertexPriv::get(nv);
		nvp->finished=false;
		nvp->neighbors.push_back({ne, false});
		addVertex(nv);

		edges.emplace_back(ne);
		oldedges.emplace_back(e, e.index());
		if(curPos().edge==e && curPos().index==ei) {
			setCurPos({ne, 0, Point{}});
		}
		if(tgtPos().edge==e && tgtPos().index==ei) {
			setTgtPos({ne, 0, Point{}});
		}
		mapEdge(e, ne, ei+1, false);
	}
	void removeEdgeRight(Edge e, size_t ei) {
		auto ne=EdgePriv::alloc();
		auto nep=EdgePriv::get(ne);
		nep->type=e.type();
		for(size_t i=1; i<=ei; i++) {
			nep->points.push_back(e.points()[i-1]);
		}
		updateBuffer(nep);

		Vertex v0=VertexPriv::alloc();
		auto vp0=VertexPriv::get(v0);
		vp0->finished=e.leftVertex().finished();
		for(auto& p: e.leftVertex().neighbors()) {
			if(p.first==e)
				vp0->neighbors.push_back({ne, false});
			else
				vp0->neighbors.push_back(p);
		}
		replaceVertex(e.leftVertex(), v0);

		std::vector<std::pair<Edge, bool>> adjs1{};
		for(auto& p: e.rightVertex().neighbors()) {
			if(p.first!=e)
				adjs1.push_back(p);
		}
		if(adjs1.size()>0) {
			Vertex v1=VertexPriv::alloc();
			auto vp1=VertexPriv::get(v1);
			vp1->finished=false;
			vp1->neighbors=std::move(adjs1);
			replaceVertex(e.rightVertex(), v1);

			auto& adj=vp1->neighbors[0];
			auto adji=adj.second?adj.first.points().size()-1:0;
			if(curPos().edge==e && curPos().index==e.points().size()-1) {
				setCurPos({adj.first, adji, Point{}});
			}
			if(tgtPos().edge==e && tgtPos().index==e.points().size()-1) {
				setTgtPos({adj.first, adji, Point{}});
			}
		} else {
			removeVertex(e.rightVertex());
		}

		auto nv=VertexPriv::alloc();
		auto nvp=VertexPriv::get(nv);
		nvp->finished=false;
		nvp->neighbors.push_back({ne, true});
		addVertex(nv);

		edges.emplace_back(ne);
		oldedges.emplace_back(e, e.index());
		if(curPos().edge==e && curPos().index==ei) {
			setCurPos({ne, ei-1, Point{}});
		}
		if(tgtPos().edge==e && tgtPos().index==ei) {
			setTgtPos({ne, ei-1, Point{}});
		}
		mapEdge(e, ne, 0, false);
	}
	void removeEdgeMid(Edge e, size_t ei0, size_t ei1) {
		auto ne0=EdgePriv::alloc();
		auto nep0=EdgePriv::get(ne0);
		nep0->type=e.type();
		for(size_t i=0; i<ei0; i++) {
			nep0->points.push_back(e.points()[i]);
		}
		updateBuffer(nep0);

		auto ne1=EdgePriv::alloc();
		auto nep1=EdgePriv::get(ne1);
		nep1->type=e.type();
		for(size_t i=ei1+1; i<e.points().size(); i++) {
			nep1->points.push_back(e.points()[i]);
		}
		updateBuffer(nep1);

		Vertex v0=VertexPriv::alloc();
		auto vp0=VertexPriv::get(v0);
		vp0->finished=e.leftVertex().finished();
		for(auto& p: e.leftVertex().neighbors()) {
			if(p.first==e)
				vp0->neighbors.push_back({ne0, false});
			else
				vp0->neighbors.push_back(p);
		}
		replaceVertex(e.leftVertex(), v0);

		Vertex v1=VertexPriv::alloc();
		auto vp1=VertexPriv::get(v1);
		vp1->finished=e.rightVertex().finished();
		for(auto& p: e.rightVertex().neighbors()) {
			if(p.first==e)
				vp1->neighbors.push_back({ne1, true});
			else
				vp1->neighbors.push_back(p);
		}
		replaceVertex(e.rightVertex(), v1);

		Vertex nv0=VertexPriv::alloc();
		auto nvp0=VertexPriv::get(nv0);
		nvp0->finished=false;
		nvp0->neighbors.push_back({ne0, true});
		addVertex(nv0);

		auto nv1=VertexPriv::alloc();
		auto nvp1=VertexPriv::get(nv1);
		nvp1->finished=false;
		nvp1->neighbors.push_back({ne1, false});
		addVertex(nv1);

		edges.emplace_back(ne0);
		edges.emplace_back(ne1);
		oldedges.emplace_back(e, e.index());
		if(curPos().edge==e) {
			if(curPos().index==ei0) {
				setCurPos({ne0, ei0-1, Point{}});
			} else if(curPos().index==ei1) {
				setCurPos({ne1, 0, Point{}});
			}
		}
		if(tgtPos().edge==e) {
			if(tgtPos().index==ei0) {
				setTgtPos({ne0, ei0-1, Point{}});
			} else if(tgtPos().index==ei1) {
				setTgtPos({ne1, 0, Point{}});
			}
		}
		mapEdgeMultiple(e, {std::make_tuple(ne0, 0, false), std::make_tuple(ne1, ei1+1, false)});
	}
	public:
	CommandRemoveEdge(SessionPriv* tp, Edge e0, size_t ei0, Edge e1, size_t ei1):
		Command{tp, "Remove edge"}, oldvertices{}, vertices{},
		oldedges{}, edges{}, soma{}, oldneurons{}
	{
		if(e0) {
			if(e1) {
				if(e0==e1) {
					size_t eil=ei0;
					size_t eir=ei1;
					if(ei0>ei1) {
						eil=ei1;
						eir=ei0;
					}
					if(eil<2) {
						if(eir+2>=e0.points().size()) {
							removeEdgeWhole(e0);
						} else {
							removeEdgeLeft(e0, eir);
						}
					} else {
						if(eir+2>=e0.points().size()) {
							removeEdgeRight(e0, eil);
						} else {
							removeEdgeMid(e0, eil, eir);
						}
					}
				} else {
					removeEdgeWhole(e1);
				}
			} else {
				removeEdgeWhole(e0);
			}
		} else {
			if(e1) {
				removeEdgeWhole(e1);
			} else {
				throwError("Nothing to remove");
			}
		}
		sortPairs();
	}
	~CommandRemoveEdge() override {
		if(performed()) {
			for(auto& p: oldvertices) {
				VertexPriv::free(p.first);
			}
			for(auto& p: oldedges) {
				auto ep=EdgePriv::get(p.first);
				freeBuffer(ep);
				EdgePriv::free(p.first);
			}
			for(auto& p: oldneurons) {
				TreePriv::free(p.first);
			}
		} else {
			for(auto v: vertices) {
				VertexPriv::free(v);
			}
			for(auto e: edges) {
				auto ep=EdgePriv::get(e);
				freeBuffer(ep);
				EdgePriv::free(e);
			}
		}
	}
};

class CommandAddEdge: public Command {
	std::vector<std::pair<Vertex, size_t>> oldvertices;
	std::vector<Vertex> vertices;
	std::vector<std::pair<Edge, size_t>> oldedges;
	std::vector<Edge> edges;
	std::vector<std::tuple<Tree, Vertex, Vertex>> soma;
	protected:
	Edge connectVertices(const std::vector<Point>& pts, Vertex v0, Vertex v1, int16_t type, bool extend, bool* extended) {
		Edge ne{};
		if(v0.neighbors().size()==1) {
			if(!v0.tree() || v0.tree().root()!=v0) {
				if(extend) {
					ne=extendEdge(v0.neighbors()[0].first, !v0.neighbors()[0].second, pts, makeVertexCopy(v1));
					*extended=true;
				}
			}
		}
		if(!ne) { // XXX e0?e1
			auto nv0=makeVertexCopy(v0);
			auto nv1=makeVertexCopy(v1);
			ne=createEdge(pts, nv0, nv1, type);
		}
		return ne;
	}
	Edge connectVertexEdgeAdj(bool back, const std::vector<Point>& pts, bool right, Edge e, size_t ei, int16_t type) {
		Vertex nv0, nv1;
		Edge ne;
		auto nv=makeVertexSplit(e, ei, &nv0, &nv1);
		if(back) {
			if(right) {
				ne=createEdge(pts, nv, nv1, type);
			} else {
				ne=createEdge(pts, nv, nv0, type);
			}
		} else {
			if(right) {
				ne=createEdge(pts, nv1, nv, type);
			} else {
				ne=createEdge(pts, nv0, nv, type);
			}
		}
		return ne;
	}
	Edge connectVertexEdgeNear(bool back, const std::vector<Point>& pts, Vertex v, Edge e, size_t ei, int16_t type, bool* extended) {
		Edge ne{};
		if(v.neighbors().size()==1) {
			if(!v.tree() || v.tree().root()!=v) {
				if(!back) {
					ne=extendAndSplitEdge(v.neighbors()[0].first, !v.neighbors()[0].second, pts, e, ei);
					*extended=true;
				}
			}
		}
		if(!ne) { // XXX e0?e1
			auto nv0=makeVertexCopy(v);
			auto nv1=makeVertexSplit(e, ei, nullptr, nullptr);
			if(back) {
				ne=createEdge(pts, nv1, nv0, type);
			} else {
				ne=createEdge(pts, nv0, nv1, type);
			}
		}
		return ne;
	}
	Edge connectVertexEdgeFar(bool back, const std::vector<Point>& pts, Vertex v, Edge e, size_t ei, int16_t type, bool* extended) {
		Edge ne{};
		if(v.neighbors().size()==1) {
			if(!v.tree() || v.tree().root()!=v) {
				if(!back) {
					auto nv=makeVertexSplit(e, ei, nullptr, nullptr);
					ne=extendEdge(v.neighbors()[0].first, !v.neighbors()[0].second, pts, nv);
					*extended=true;
				}
			}
		}
		if(!ne) { // XXX e0?e1
			auto nv0=makeVertexCopy(v);
			auto nv1=makeVertexSplit(e, ei, nullptr, nullptr);
			if(back) {
			ne=createEdge(pts, nv1, nv0, type);
			} else {
			ne=createEdge(pts, nv0, nv1, type);
			}
		}
		return ne;
	}
	Edge connectEdgesSame(const std::vector<Point>& pts, Edge e, size_t ei0, size_t ei1) {
		auto verts=makeVerticesSplit(e, ei0, ei1);
		auto ne=createEdge(pts, verts.first, verts.second, e.type());
		return ne;
	}
	Edge connectEdgesDiff(const std::vector<Point>& pts, Edge e0, size_t ei0, Edge e1, size_t ei1) {
		auto verts=makeVerticesSplit(e0, ei0, e1, ei1);
		auto ne=createEdge(pts, verts.first, verts.second, e0.type());
		return ne;
	}
	Edge connectEdgeVoid(bool back, const std::vector<Point>& pts, Edge e, size_t ei, bool* extended) {
		Edge ne{};
		Vertex v{};
		if(ei==0) {
			v=e.leftVertex();
		} else if(ei==e.points().size()-1) {
			v=e.rightVertex();
		}
		auto nv1=makeVertexNew();
		if(v) {
			if(v.neighbors().size()==1) {
				if(!v.tree() || v.tree().root()!=v) {
					if(!back) {
						ne=extendEdge(e, !ei, pts, nv1);
						*extended=true;
					}
				}
			}
			if(!ne) {
				auto nv0=makeVertexCopy(v);
				if(back) {
					ne=createEdge(pts, nv1, nv0, e.type());
				} else {
					ne=createEdge(pts, nv0, nv1, e.type());
				}
			}
		} else {
			auto nv0=makeVertexSplit(e, ei, nullptr, nullptr);
			if(back) {
				ne=createEdge(pts, nv1, nv0, e.type());
			} else {
				ne=createEdge(pts, nv0, nv1, e.type());
			}
		}
		return ne;
	}
	Edge connectVoidVoid(const std::vector<Point>& pts) {
		auto nv0=makeVertexNew();
		auto nv1=makeVertexNew();
		auto ne=createEdge(pts, nv0, nv1, 0);
		return ne;
	}
	CommandAddEdge(SessionPriv* tp, const QString& t):
		Command{tp, t}, oldvertices{}, vertices{},
		oldedges{}, edges{}, soma{}
	{
	}
	void sortPairs() {
		std::sort(oldvertices.begin(), oldvertices.end(), [](const std::pair<Vertex, size_t>& a, const std::pair<Vertex, size_t>& b) { return a.second<b.second; });
		std::sort(oldedges.begin(), oldedges.end(), [](const std::pair<Edge, size_t>& a, const std::pair<Edge, size_t>& b) { return a.second<b.second; });
		//std::sort(oldneurons.begin(), oldneurons.end(), [](const std::pair<Tree, size_t>& a, const std::pair<Tree, size_t>& b) { return a.second<b.second; });
	}
	void replaceVertex(Vertex v0, Vertex v1) {
		oldvertices.emplace_back(v0, v0.index());
		vertices.emplace_back(v1);
		if(v0.tree() && v0.tree().root()==v0) {
			soma.emplace_back(v0.tree(), v0, v1);
		}
	}
	void addVertex(Vertex v0) {
		vertices.push_back(v0);
	}
	void addEdge(Edge e) {
		edges.push_back(e);
	}
	void removeVertex(Vertex v0) {
		oldvertices.emplace_back(v0, v0.index());
		//if(v0.tree() && v0.tree().soma()==v0) {
			//oldneurons.emplace_back(v0.tree(), v0.tree().index());
		//}
	}
	std::pair<Vertex, Vertex> makeVerticesSplit(Edge e0, size_t ei0, Edge e1, size_t ei1) {
		Vertex nv0=VertexPriv::alloc();
		auto nvp0=VertexPriv::get(nv0);
		nvp0->finished=false;
		Vertex nv1=VertexPriv::alloc();
		auto nvp1=VertexPriv::get(nv1);
		nvp1->finished=false;

		Edge e00=EdgePriv::alloc();
		auto ep00=EdgePriv::get(e00);
		ep00->type=e0.type();
		ep00->vaoi=-1;
		for(size_t i=0; i<=ei0; i++) {
			ep00->points.push_back(e0.points()[i]);
		}
		updateBuffer(ep00);
		Edge e01=EdgePriv::alloc();
		auto ep01=EdgePriv::get(e01);
		ep01->type=e0.type();
		ep01->vaoi=-1;
		for(size_t i=ei0; i<e0.points().size(); i++) {
			ep01->points.push_back(e0.points()[i]);
		}
		updateBuffer(ep01);
		oldedges.emplace_back(e0, e0.index());
		edges.emplace_back(e00);
		edges.emplace_back(e01);
		mapEdgeMultiple(e0, {std::make_tuple(e00, 0, false), std::make_tuple(e01, ei0, false)});

		Edge e10=EdgePriv::alloc();
		auto ep10=EdgePriv::get(e10);
		ep10->type=e1.type();
		ep10->vaoi=-1;
		for(size_t i=0; i<=ei1; i++) {
			ep10->points.push_back(e1.points()[i]);
		}
		updateBuffer(ep10);
		Edge e11=EdgePriv::alloc();
		auto ep11=EdgePriv::get(e11);
		ep11->type=e1.type();
		ep11->vaoi=-1;
		for(size_t i=ei1; i<e1.points().size(); i++) {
			ep11->points.push_back(e1.points()[i]);
		}
		updateBuffer(ep11);
		oldedges.emplace_back(e1, e1.index());
		edges.emplace_back(e10);
		edges.emplace_back(e11);
		mapEdgeMultiple(e1, {std::make_tuple(e10, 0, false), std::make_tuple(e11, ei0, false)});

		std::vector<Vertex> verts{e0.leftVertex(), e0.rightVertex(), e1.leftVertex(), e1.rightVertex()};
		std::sort(verts.begin(), verts.end(), [](Vertex a, Vertex b) { return a.index()<b.index(); });
		auto uniq_end=std::unique(verts.begin(), verts.end());
		for(auto i=verts.begin(); i!=uniq_end; i++) {
			Vertex nv=VertexPriv::alloc();
			auto nvp=VertexPriv::get(nv);
			nvp->finished=i->finished();
			for(auto& p: i->neighbors()) {
				if(p.first==e0) {
					if(p.second) {
						nvp->neighbors.push_back({e01, true});
					} else {
						nvp->neighbors.push_back({e00, false});
					}
				} else if(p.first==e1) {
					if(p.second) {
						nvp->neighbors.push_back({e11, true});
					} else {
						nvp->neighbors.push_back({e10, false});
					}
				} else
					nvp->neighbors.push_back(p);
			}
			replaceVertex(*i, nv);
		}


		nvp0->neighbors.push_back({e00, true});
		nvp0->neighbors.push_back({e01, false});
		addVertex(nv0);
		nvp1->neighbors.push_back({e10, true});
		nvp1->neighbors.push_back({e11, false});
		addVertex(nv1);
		return {nv0, nv1};
	}
	std::pair<Vertex, Vertex> makeVerticesSplit(Edge e, size_t ei0, size_t ei1) {
		bool dir=false;
		size_t eil=ei0;
		size_t eir=ei1;
		if(ei0>ei1) {
			dir=true;
			eil=ei1;
			eir=ei0;
		}

		Vertex vl=VertexPriv::alloc();
		auto vpl=VertexPriv::get(vl);
		vpl->finished=false;
		Vertex vr=VertexPriv::alloc();
		auto vpr=VertexPriv::get(vr);
		vpr->finished=false;

		Edge e0=EdgePriv::alloc();
		auto ep0=EdgePriv::get(e0);
		ep0->type=e.type();
		ep0->vaoi=-1;
		for(size_t i=0; i<=eil; i++) {
			ep0->points.push_back(e.points()[i]);
		}
		updateBuffer(ep0);
		Edge e1=EdgePriv::alloc();
		auto ep1=EdgePriv::get(e1);
		ep1->type=e.type();
		ep1->vaoi=-1;
		for(size_t i=eil; i<=eir; i++) {
			ep1->points.push_back(e.points()[i]);
		}
		updateBuffer(ep1);
		Edge e2=EdgePriv::alloc();
		auto ep2=EdgePriv::get(e2);
		ep2->type=e.type();
		ep2->vaoi=-1;
		for(size_t i=eir; i<e.points().size(); i++) {
			ep2->points.push_back(e.points()[i]);
		}
		updateBuffer(ep2);
		oldedges.emplace_back(e, e.index());
		edges.emplace_back(e0);
		edges.emplace_back(e1);
		edges.emplace_back(e2);
		mapEdgeMultiple(e, {std::make_tuple(e0, 0, false),
				std::make_tuple(e1, eil, false),
				std::make_tuple(e2, eir, false),
				});

		Vertex v0=VertexPriv::alloc();
		auto vp0=VertexPriv::get(v0);
		vp0->finished=e.leftVertex().finished();
		for(auto& p: e.leftVertex().neighbors()) {
			if(p.first==e)
				vp0->neighbors.push_back({e0, false});
			else
				vp0->neighbors.push_back(p);
		}
		replaceVertex(e.leftVertex(), v0);
		Vertex v1=VertexPriv::alloc();
		auto vp1=VertexPriv::get(v1);
		vp1->finished=e.rightVertex().finished();
		for(auto& p: e.rightVertex().neighbors()) {
			if(p.first==e)
				vp1->neighbors.push_back({e2, true});
			else
				vp1->neighbors.push_back(p);
		}
		replaceVertex(e.rightVertex(), v1);

		vpl->neighbors.push_back({e0, true});
		vpl->neighbors.push_back({e1, false});
		addVertex(vl);
		vpr->neighbors.push_back({e1, true});
		vpr->neighbors.push_back({e2, false});
		addVertex(vr);
		if(dir)
			return {vr, vl};
		return {vl, vr};
	}
Vertex makeVertexSplit(Edge e, size_t ei, Vertex* pv0, Vertex* pv1) {
	Vertex v=VertexPriv::alloc();
	auto vp=VertexPriv::get(v);
	vp->finished=false;
	Edge e0=EdgePriv::alloc();
	auto ep0=EdgePriv::get(e0);
	ep0->type=e.type();
	ep0->vaoi=-1;
	for(size_t i=0; i<=ei; i++) {
		ep0->points.push_back(e.points()[i]);
	}
	updateBuffer(ep0);
	Edge e1=EdgePriv::alloc();
	auto ep1=EdgePriv::get(e1);
	ep1->type=e.type();
	ep1->vaoi=-1;
	for(size_t i=ei; i<e.points().size(); i++) {
		ep1->points.push_back(e.points()[i]);
	}
	updateBuffer(ep1);
	oldedges.emplace_back(e, e.index());
	edges.emplace_back(e0);
	edges.emplace_back(e1);
	mapEdgeMultiple(e, {std::make_tuple(e0, 0, false), std::make_tuple(e1, ei, false)});

	Vertex v0=VertexPriv::alloc();
	auto vp0=VertexPriv::get(v0);
	vp0->finished=e.leftVertex().finished();
	for(auto& p: e.leftVertex().neighbors()) {
		if(p.first==e)
			vp0->neighbors.push_back({e0, false});
		else
			vp0->neighbors.push_back(p);
	}
	replaceVertex(e.leftVertex(), v0);
	Vertex v1=VertexPriv::alloc();
	auto vp1=VertexPriv::get(v1);
	vp1->finished=e.rightVertex().finished();
	for(auto& p: e.rightVertex().neighbors()) {
		if(p.first==e)
			vp1->neighbors.push_back({e1, true});
		else
			vp1->neighbors.push_back(p);
	}
	replaceVertex(e.rightVertex(), v1);
	if(pv0) *pv0=v0;
	if(pv1) *pv1=v1;

	vp->neighbors.push_back({e0, true});
	vp->neighbors.push_back({e1, false});
	addVertex(v);
	return v;
}
	Vertex makeVertexNew() {
		Vertex v=VertexPriv::alloc();
		auto vp=VertexPriv::get(v);
		vp->finished=false;
		addVertex(v);
		return v;
	}
Vertex makeVertexCopy(Vertex vprev) {
	Vertex v=VertexPriv::alloc();
	auto vp=VertexPriv::get(v);
	vp->finished=false;
	for(auto& p: vprev.neighbors()) {
		vp->neighbors.push_back(p);
	}
	replaceVertex(vprev, v);
	return v;
}
Edge createEdge(const std::vector<Point>& pts, Vertex nv0, Vertex nv1, int16_t type) { // XXX when tgte already linked to e
	auto e=EdgePriv::alloc();
	auto ep=EdgePriv::get(e);
	ep->type=type;
	ep->vaoi=-1;
	for(size_t i=0; i<pts.size(); i++)
		ep->points.push_back(pts[i]);
	updateBuffer(ep);
	addEdge(e);
	auto vp0=VertexPriv::get(nv0);
	auto vp1=VertexPriv::get(nv1);
	vp0->neighbors.push_back({e, false});
	vp1->neighbors.push_back({e, true});
	//
	//
	//Position p{e, 0, {}};
	//setCurPos(p);
	//
	// ensure not soma node
	return e;
}
Edge extendAndSplitEdge(Edge e0, bool e0dir, const std::vector<Point>& pts, Edge e1,size_t ei1) { // XXX when tgte already linked to e
	// ensure not soma node
	Edge ne0=EdgePriv::alloc();
	auto nep0=EdgePriv::get(ne0);
	nep0->type=e1.type();
	nep0->vaoi=-1;
	for(size_t i=0; i<=ei1; i++) {
		nep0->points.push_back(e1.points()[i]);
	}
	updateBuffer(nep0);
	Edge ne1=EdgePriv::alloc();
	auto nep1=EdgePriv::get(ne1);
	nep1->type=e1.type();
	nep1->vaoi=-1;
	for(size_t i=ei1; i<e1.points().size(); i++) {
		nep1->points.push_back(e1.points()[i]);
	}
	updateBuffer(nep1);
	oldedges.emplace_back(e1, e1.index());
	edges.emplace_back(ne0);
	edges.emplace_back(ne1);
	mapEdgeMultiple(e1, {std::make_tuple(ne0, 0, false), std::make_tuple(ne1, ei1, false)});

	Edge ne=EdgePriv::alloc();
	auto nep=EdgePriv::get(ne);
	nep->type=e0.type();
	nep->vaoi=-1;
	Vertex v0, v1;
	if(e0dir) {
		for(size_t i=e0.points().size(); i>0; i--) {
			nep->points.push_back(e0.points()[i-1]);
		}
		v0=e0.rightVertex();
		v1=e0.leftVertex();
	} else {
		for(size_t i=0; i<e0.points().size(); i++) {
			nep->points.push_back(e0.points()[i]);
		}
		v0=e0.leftVertex();
		v1=e0.rightVertex();
	}
	for(size_t i=1; i<pts.size(); i++) {
		nep->points.push_back(pts[i]);
	}
	updateBuffer(nep);
	mapEdge(e0, ne, e0dir?e0.points().size()-1:0, e0dir);
	oldedges.emplace_back(e0, e0.index());
	edges.emplace_back(ne);

	Vertex nv0=VertexPriv::alloc();
	auto nvp0=VertexPriv::get(nv0);
	nvp0->finished=e1.leftVertex().finished();
	for(auto& p: e1.leftVertex().neighbors()) {
		if(p.first==e1)
			nvp0->neighbors.push_back({ne0, false});
		else if(p.first==e0)
			nvp0->neighbors.push_back({ne, false});
		else
			nvp0->neighbors.push_back(p);
	}
	replaceVertex(e1.leftVertex(), nv0);
	Vertex nv1=VertexPriv::alloc();
	auto nvp1=VertexPriv::get(nv1);
	nvp1->finished=e1.rightVertex().finished();
	for(auto& p: e1.rightVertex().neighbors()) {
		if(p.first==e1)
			nvp1->neighbors.push_back({ne1, true});
		else if(p.first==e0)
			nvp1->neighbors.push_back({ne, false});
		else
			nvp1->neighbors.push_back(p);
	}
	replaceVertex(e1.rightVertex(), nv1);

	Vertex nv=VertexPriv::alloc();
	auto nvp=VertexPriv::get(nv);
	nvp->finished=false;
	nvp->neighbors.push_back({ne0, true});
	nvp->neighbors.push_back({ne1, false});
	nvp->neighbors.push_back({ne, true});
	addVertex(nv);
	removeVertex(v1);
	return ne;
}
Edge extendEdge(Edge e, bool dir, const std::vector<Point>& pts, Vertex nv1) { // XXX when tgte already linked to e
	// ensure not soma node
	Edge ne=EdgePriv::alloc();
	auto nep=EdgePriv::get(ne);
	nep->type=e.type();
	nep->vaoi=-1;
	Vertex v0, v1;
	if(dir) {
		for(size_t i=e.points().size(); i>0; i--) {
			nep->points.push_back(e.points()[i-1]);
		}
		v0=e.rightVertex();
		v1=e.leftVertex();
	} else {
		for(size_t i=0; i<e.points().size(); i++) {
			nep->points.push_back(e.points()[i]);
		}
		v0=e.leftVertex();
		v1=e.rightVertex();
	}
	for(size_t i=1; i<pts.size(); i++) {
		nep->points.push_back(pts[i]);
	}
	mapEdge(e, ne, dir?e.points().size()-1:0, dir);
	updateBuffer(nep);
	oldedges.emplace_back(e, e.index());
	edges.emplace_back(ne);
	//
	auto nvp1=VertexPriv::get(nv1);
	nvp1->neighbors.push_back({ne, true});
	auto nv0=VertexPriv::alloc();
	auto nvp0=VertexPriv::get(nv0);
	nvp0->finished=v0.finished();
	for(auto& p: v0.neighbors()) {
		if(p.first==e)
			nvp0->neighbors.push_back({ne, false});
		else
			nvp0->neighbors.push_back(p);
	}
	replaceVertex(v0, nv0);
	removeVertex(v1);
	return ne;
}
	public:
	void undoImp(SessionPriv* tp) override {
		auto gp=GraphPriv::get(graph());
		size_t shift=0;
		for(auto v: vertices) {
			gp->vertices.erase(gp->vertices.begin()+v.index()-shift);
			shift++;
		}
		for(auto& p: oldvertices) {
			gp->vertices.insert(gp->vertices.begin()+p.second, p.first);
			for(auto& pp: p.first.neighbors()) {
				auto ep=EdgePriv::get(pp.first);
				if(pp.second) {
					ep->rightVertex=p.first;
				} else {
					ep->leftVertex=p.first;
				}
			}
		}
		shift=0;
		for(auto e: edges) {
			gp->edges.erase(gp->edges.begin()+e.index()-shift);
			shift++;
		}
		for(auto& p: oldedges) {
			gp->edges.insert(gp->edges.begin()+p.second, p.first);
		}
		for(auto& t: soma) {
			auto np=TreePriv::get(std::get<0>(t));
			np->root=std::get<1>(t);
		}
		/*
		for(auto& p: oldneurons) {
			auto np=TreePriv::get(p.first);
			np->completed=false;
			np->selected=false;
			model()->beginInsertRow(p.second);
			gp->trees.insert(gp->trees.begin()+p.second, p.first);
			model()->endInsertRow();
		}
		*/
		gp->updateModel();
		for(auto n: gp->trees) {
			auto np=TreePriv::get(n);
			if(np->completed!=np->completed_prev)
				model()->emitDataChanged(np->index, 0, 0);
		}
	}
	void redoImp(SessionPriv* tp) override {
		auto gp=GraphPriv::get(graph());
		size_t shift=0;
		/*
		for(auto& p: oldneurons) {
			model()->beginRemoveRow(p.second-shift);
			gp->trees.erase(gp->trees.begin()+p.second-shift);
			model()->endRemoveRow();
			shift++;
		}
		*/
		for(auto& t: soma) {
			auto np=TreePriv::get(std::get<0>(t));
			np->root=std::get<2>(t);
		}
		shift=0;
		for(auto& p: oldedges) {
			gp->edges.erase(gp->edges.begin()+p.second-shift);
			shift++;
		}
		for(auto e: edges) {
			gp->edges.push_back(e);
		}
		shift=0;
		for(auto& p: oldvertices) {
			gp->vertices.erase(gp->vertices.begin()+p.second-shift);
			shift++;
		}
		for(auto v: vertices) {
			gp->vertices.push_back(v);
			for(auto& p: v.neighbors()) {
				auto ep=EdgePriv::get(p.first);
				if(p.second) {
					ep->rightVertex=v;
				} else {
					ep->leftVertex=v;
				}
			}
		}
		gp->updateModel();
		for(auto n: gp->trees) {
			auto np=TreePriv::get(n);
			if(np->completed!=np->completed_prev)
				model()->emitDataChanged(np->index, 0, 0);
		}
	}
	~CommandAddEdge() override {
		if(performed()) {
			for(auto& p: oldvertices) {
				VertexPriv::free(p.first);
			}
			for(auto& p: oldedges) {
				auto ep=EdgePriv::get(p.first);
				freeBuffer(ep);
				EdgePriv::free(p.first);
			}
			/*
			for(auto& p: oldneurons) {
				TreePriv::free(p.first);
			}
			*/
		} else {
			for(auto v: vertices) {
				VertexPriv::free(v);
			}
			for(auto e: edges) {
				auto ep=EdgePriv::get(e);
				freeBuffer(ep);
				EdgePriv::free(e);
			}
		}
	}
};

class CommandCreateBranch: public CommandAddEdge {
	public:
	CommandCreateBranch(SessionPriv* tp, const Path& path):
		CommandAddEdge{tp, "Create branch"}
	{
		Edge ne{};
		bool extended=false;
		if(path.edge0) {
			if(path.edge1) {
				Vertex v0{};
				if(path.index0==0)
					v0=path.edge0.leftVertex();
				else if(path.index0==path.edge0.points().size()-1)
					v0=path.edge0.rightVertex();
				Vertex v1{};
				if(path.index1==0)
					v1=path.edge1.leftVertex();
				else if(path.index1==path.edge1.points().size()-1)
					v1=path.edge1.rightVertex();
				if(v0) {
					if(v1) {
						if(v0==v1)
							throwError("Should not occure");
						bool sameEdge=false;
						for(auto& p: v0.neighbors()) {
							if((p.second?p.first.leftVertex():p.first.rightVertex())==v1)
								sameEdge=true;
						}
						if(sameEdge) {
							ne=connectVertices(path.points, v0, v1, path.edge0.type(), false, &extended);
							// newedge0 nullifonpath, newedgeN null
						} else {
							ne=connectVertices(path.points, v0, v1, path.edge0.type(), true, &extended);
							// newedgei nullifonpath, newedgeN null
						}
					} else {
						if(v0==path.edge1.leftVertex() || v0==path.edge1.rightVertex()) {
							ne=connectVertexEdgeAdj(false, path.points, v0==path.edge1.rightVertex(), path.edge1, path.index1, path.edge0.type());
							// newedg0 nullifonpath, newedgeN null
						} else {
							bool nearBy=false;
							for(auto& p: v0.neighbors()) {
								auto v=p.second?p.first.leftVertex():p.first.rightVertex();
								if(v==path.edge1.leftVertex() || v==path.edge1.rightVertex())
									nearBy=true;
							}
							if(nearBy) {
								ne=connectVertexEdgeNear(false, path.points, v0, path.edge1, path.index1, path.edge0.type(), &extended);
								// newedg0
							} else {
								ne=connectVertexEdgeFar(false, path.points, v0, path.edge1, path.index1, path.edge0.type(), &extended);
							}
						}
					}
				} else {
					if(v1) {
						if(v1==path.edge0.leftVertex() || v1==path.edge0.rightVertex()) {
							ne=connectVertexEdgeAdj(false, path.points, v1==path.edge0.rightVertex(), path.edge0, path.index0, path.edge0.type());
							// newedg0 nullifonpath, newedgeN null
						} else {
							bool nearBy=false;
							for(auto& p: v1.neighbors()) {
								auto v=p.second?p.first.leftVertex():p.first.rightVertex();
								if(v==path.edge0.leftVertex() || v==path.edge0.rightVertex())
									nearBy=true;
							}
							if(nearBy) {
								ne=connectVertexEdgeNear(true, path.points, v1, path.edge0, path.index0, path.edge1.type(), &extended);
								// newedg0
							} else {
								ne=connectVertexEdgeFar(true, path.points, v1, path.edge0, path.index0, path.edge1.type(), &extended);
							}
						}
					} else {
						if(path.edge0==path.edge1) {
							if(path.index0==path.index1)
								throwError("Should not occure");
							ne=connectEdgesSame(path.points, path.edge0, path.index0, path.index1);
						} else {
							ne=connectEdgesDiff(path.points, path.edge0, path.index0, path.edge1, path.index1);
						/* if(commv(path.edge1, e2)==2) {
								split_2_loop new_edge
							} else if(commv(e1, e2)==1) {
								split_2_edges new_edge
							} else {
								make_vert new_edge make_vert
							} */
						}
					}
				}
			} else {
				ne=connectEdgeVoid(false, path.points, path.edge0, path.index0, &extended);
			}
		} else {
			if(path.edge1) {
				ne=connectEdgeVoid(true, path.points, path.edge1, path.index1, &extended);
			} else {
				ne=connectVoidVoid(path.points);
			}
		}
		sortPairs();

		if(isTargetPosOnPath()) {
			Position pp{Edge{}, SIZE_MAX, Point{}};
			setTgtPos(pp);
		}
		if(!extended) {
			double mindist=INFINITY;
			size_t nearesti=SIZE_MAX;
			auto curp=curPos().point;
			if(curPos().edge) {
				curp=curPos().edge.points()[curPos().index];
			}
			for(size_t i=0; i<ne.points().size(); i++) {
				auto d=ne.points()[i].distTo(curp);
				if(d<mindist) {
					mindist=d;
					nearesti=i;
				}
			}
			Position p{ne, nearesti, Point{}};
			setCurPos(p);
		}
	}
};

class CommandExtendBranch: public CommandAddEdge {
	public:
		CommandExtendBranch(SessionPriv* tp, const Path& path):
			CommandAddEdge{tp, "Extend branch"}
		{
			Edge ne{};
			bool extended=false;
			if(path.edge0) {
				if(path.edge1) {
					Vertex v0{};
					if(path.index0==0)
						v0=path.edge0.leftVertex();
					else if(path.index0==path.edge0.points().size()-1)
						v0=path.edge0.rightVertex();
					Vertex v1{};
					if(path.index1==0)
						v1=path.edge1.leftVertex();
					else if(path.index1==path.edge1.points().size()-1)
						v1=path.edge1.rightVertex();
					if(v0) {
						if(v1) {
							if(v0==v1)
								throwError("Should not occure");
							bool sameEdge=false;
							for(auto& p: v0.neighbors()) {
								if((p.second?p.first.leftVertex():p.first.rightVertex())==v1)
									sameEdge=true;
							}
							if(sameEdge) {
								ne=connectVertices(path.points, v0, v1, path.edge0.type(), false, &extended);
								// newedge0 nullifonpath, newedgeN null
							} else {
								ne=connectVertices(path.points, v0, v1, path.edge0.type(), true, &extended);
								// newedgei nullifonpath, newedgeN null
							}
						} else {
							if(v0==path.edge1.leftVertex() || v0==path.edge1.rightVertex()) {
								ne=connectVertexEdgeAdj(false, path.points, v0==path.edge1.rightVertex(), path.edge1, path.index1, path.edge0.type());
								// newedg0 nullifonpath, newedgeN null
							} else {
								bool nearBy=false;
								for(auto& p: v0.neighbors()) {
									auto v=p.second?p.first.leftVertex():p.first.rightVertex();
									if(v==path.edge1.leftVertex() || v==path.edge1.rightVertex())
										nearBy=true;
								}
								if(nearBy) {
									ne=connectVertexEdgeNear(false, path.points, v0, path.edge1, path.index1, path.edge0.type(), &extended);
									// newedg0
								} else {
									ne=connectVertexEdgeFar(false, path.points, v0, path.edge1, path.index1, path.edge0.type(), &extended);
								}
							}
						}
					} else {
						if(v1) {
							if(v1==path.edge0.leftVertex() || v1==path.edge0.rightVertex()) {
								ne=connectVertexEdgeAdj(false, path.points, v1==path.edge0.rightVertex(), path.edge0, path.index0, path.edge0.type());
								// newedg0 nullifonpath, newedgeN null
							} else {
								bool nearBy=false;
								for(auto& p: v1.neighbors()) {
									auto v=p.second?p.first.leftVertex():p.first.rightVertex();
									if(v==path.edge0.leftVertex() || v==path.edge0.rightVertex())
										nearBy=true;
								}
								if(nearBy) {
									ne=connectVertexEdgeNear(true, path.points, v1, path.edge0, path.index0, path.edge1.type(), &extended);
									// newedg0
								} else {
									ne=connectVertexEdgeFar(true, path.points, v1, path.edge0, path.index0, path.edge1.type(), &extended);
								}
							}
						} else {
							if(path.edge0==path.edge1) {
								if(path.index0==path.index1)
									throwError("Should not occure");
								ne=connectEdgesSame(path.points, path.edge0, path.index0, path.index1);
							} else {
								ne=connectEdgesDiff(path.points, path.edge0, path.index0, path.edge1, path.index1);
								/* if(commv(path.edge1, e2)==2) {
									split_2_loop new_edge
									} else if(commv(path.edge1, e2)==1) {
									split_2_edges new_edge
									} else {
									make_vert new_edge make_vert
									} */
							}
						}
					}
				} else {
					ne=connectEdgeVoid(false, path.points, path.edge0, path.index0, &extended);
				}
			} else {
				if(path.edge1) {
					ne=connectEdgeVoid(true, path.points, path.edge1, path.index1, &extended);
				} else {
					ne=connectVoidVoid(path.points);
				}
			}
			sortPairs();
			Position p{ne, ne.points().size()-1, Point{}};
			setCurPos(p);
			Position pp{Edge{}, SIZE_MAX, Point{}};
			setTgtPos(pp);
		}
};
class CommandImportSwc: public Command {
	std::vector<Vertex> vertices;
	std::vector<Edge> edges;
	std::vector<Tree> neurons;
	//
	void replaceVertex(Vertex v0, Vertex v1) {
		vertices.emplace_back(v1);
	}
	public:
	CommandImportSwc(SessionPriv* tp, const std::vector<Point>& points, const std::vector<int64_t>& parents):
		Command{tp, "Import SWC"}, vertices{},
		edges{}, neurons{}
	{
		auto N=points.size();
		std::vector<int> degree(N, 0);
		for(size_t id=0; id<N; id++) {
			auto par=parents[id];
			if(par!=-1) {
				degree[id]++;
				degree[par]++;
			}
		}
		std::vector<Vertex> id2verts(N, Vertex{});
		for(size_t id=0; id<N; id++) {
			bool node=false;
			bool soma=false;
			auto par=parents[id];
			if(par==-1) {
				node=true;
				if(points[id].m==1) {
					soma=true;
				}
			} else {
				switch(degree[id]) {
					case 2:
						break;
					default:
						node=true;
						break;
				}
			}
			if(node) {
				auto v=VertexPriv::alloc();
				auto vp=VertexPriv::get(v);
				vp->finished=true;
				id2verts[id]=v;
				vertices.push_back(v);
				if(soma) {
					auto n=TreePriv::alloc();
					auto np=TreePriv::get(n);
					np->root=v;
					int i=0;
					auto name=QString{"Imported Neuron %1"}.arg(i).toStdString();
					while(true) {
						bool hit=false;
						for(auto n: graph().trees()) {
							if(n.name()==name) {
								hit=true;
								break;
							}
						}
						for(auto n: neurons) {
							if(n.name()==name) {
								hit=true;
								break;
							}
						}
						if(!hit) break;
						i++;
						name=QString{"Imported Neuron %1"}.arg(i).toStdString();
					}
					np->name=name;
					neurons.push_back(n);
				}
			}
		}
		// !1,2..., !2, *3+, *1
		for(size_t id=0; id<N; id++) {
			auto par=parents[id];
			if(par!=-1 && degree[id]!=2) {
				auto e=EdgePriv::alloc();
				auto ep=EdgePriv::get(e);
				int64_t i=id;
				ep->points.push_back(points.at(i));
				int64_t j=par;
				auto vert=id2verts[j];
				while(!vert) {
					i=j;
					ep->points.push_back(points.at(i));
					j=parents.at(i);
					vert=id2verts[j];
				}
				ep->points.push_back(points.at(j));
				ep->leftVertex=id2verts[id];
				auto vp=VertexPriv::get(ep->leftVertex);
				vp->neighbors.push_back({e, false});
				ep->rightVertex=vert;
				vp=VertexPriv::get(ep->rightVertex);
				vp->neighbors.push_back({e, true});
				std::array<int, 5> types;
				for(auto& v: types) v=0;
				for(auto& pt: ep->points) {
					if(pt.m>=0 && pt.m<5) {
						types[pt.m]++;
						pt.m=0;
					} else if(pt.m<0) {
						pt.m=0;
					} else {
						pt.m=pt.m-4;
					}
				}
				ep->type=0;
				int max=types[0];
				for(size_t i=2; i<types.size(); i++) {
					if(types[i]>max) {
						max=types[i];
						ep->type=i;
					}
				}
				updateBuffer(ep);
				edges.push_back(e);
			}
		}
	}
	void undoImp(SessionPriv* tp) override {
		auto gp=GraphPriv::get(graph());
		size_t shift=0;
		for(auto p: neurons) {
			model()->beginRemoveRow(p.index()-shift);
			gp->trees.erase(gp->trees.begin()+p.index()-shift);
			model()->endRemoveRow();
			shift++;
		}
		shift=0;
		for(auto v: vertices) {
			gp->vertices.erase(gp->vertices.begin()+v.index()-shift);
			shift++;
		}
		shift=0;
		for(auto e: edges) {
			gp->edges.erase(gp->edges.begin()+e.index()-shift);
			shift++;
		}
		gp->updateModel();
	}
	void redoImp(SessionPriv* tp) override {
		auto gp=GraphPriv::get(graph());
		for(auto e: edges) {
			gp->edges.push_back(e);
		}
		for(auto v: vertices) {
			gp->vertices.push_back(v);
			for(auto& p: v.neighbors()) {
				auto ep=EdgePriv::get(p.first);
				if(p.second) {
					ep->rightVertex=v;
				} else {
					ep->leftVertex=v;
				}
			}
		}
		for(auto p: neurons) {
			model()->beginInsertRow(gp->trees.size());
			gp->trees.push_back(p);
			model()->endInsertRow();
		}
		gp->updateModel();
	}
	~CommandImportSwc() override {
		if(!performed()) {
			for(auto v: vertices) {
				VertexPriv::free(v);
			}
			for(auto e: edges) {
				auto ep=EdgePriv::get(e);
				freeBuffer(ep);
				EdgePriv::free(e);
			}
			for(auto n: neurons) {
				TreePriv::free(n);
			}
		}
	}
};

class CommandImportFnt: public Command {
	std::vector<Vertex> vertices;
	std::vector<Edge> edges;
	std::vector<Tree> neurons;
	//
	void replaceVertex(Vertex v0, Vertex v1) {
		vertices.emplace_back(v1);
	}
	public:
	CommandImportFnt(SessionPriv* tp, const std::vector<bool>& vertInfo, const std::vector<std::tuple<size_t, size_t, int16_t, std::vector<Point>>>& edgInfo, const std::vector<std::tuple<size_t, std::string>>& somaInfo):
		Command{tp, "Import FNT"}, vertices{},
		edges{}, neurons{}
	{
		for(size_t i=0; i<vertInfo.size(); i++) {
			auto v=VertexPriv::alloc();
			auto vp=VertexPriv::get(v);
			vp->finished=vertInfo[i];
			vertices.push_back(v);
		}
		for(size_t i=0; i<somaInfo.size(); i++) {
			auto n=TreePriv::alloc();
			auto np=TreePriv::get(n);
			np->root=vertices[std::get<0>(somaInfo[i])];
			np->name=std::get<1>(somaInfo[i]);
			neurons.push_back(n);
		}
		for(size_t i=0; i<edgInfo.size(); i++) {
			auto e=EdgePriv::alloc();
			auto ep=EdgePriv::get(e);
			ep->points=std::move(std::get<3>(edgInfo[i]));
			ep->leftVertex=vertices[std::get<0>(edgInfo[i])];
			auto vp=VertexPriv::get(ep->leftVertex);
			vp->neighbors.push_back({e, false});
			ep->rightVertex=vertices[std::get<1>(edgInfo[i])];
			vp=VertexPriv::get(ep->rightVertex);
			vp->neighbors.push_back({e, true});
			ep->type=std::get<2>(edgInfo[i]);
			updateBuffer(ep);
			edges.push_back(e);
		}
	}
	void undoImp(SessionPriv* tp) override {
		auto gp=GraphPriv::get(graph());
		size_t shift=0;
		for(auto p: neurons) {
			model()->beginRemoveRow(p.index()-shift);
			gp->trees.erase(gp->trees.begin()+p.index()-shift);
			model()->endRemoveRow();
			shift++;
		}
		shift=0;
		for(auto v: vertices) {
			gp->vertices.erase(gp->vertices.begin()+v.index()-shift);
			shift++;
		}
		shift=0;
		for(auto e: edges) {
			gp->edges.erase(gp->edges.begin()+e.index()-shift);
			shift++;
		}
		gp->updateModel();
	}
	void redoImp(SessionPriv* tp) override {
		auto gp=GraphPriv::get(graph());
		for(auto e: edges) {
			gp->edges.push_back(e);
		}
		for(auto v: vertices) {
			gp->vertices.push_back(v);
			for(auto& p: v.neighbors()) {
				auto ep=EdgePriv::get(p.first);
				if(p.second) {
					ep->rightVertex=v;
				} else {
					ep->leftVertex=v;
				}
			}
		}
		for(auto p: neurons) {
			model()->beginInsertRow(gp->trees.size());
			gp->trees.push_back(p);
			model()->endInsertRow();
		}
		gp->updateModel();
	}
	~CommandImportFnt() override {
		if(!performed()) {
			for(auto v: vertices) {
				VertexPriv::free(v);
			}
			for(auto e: edges) {
				auto ep=EdgePriv::get(e);
				freeBuffer(ep);
				EdgePriv::free(e);
			}
			for(auto n: neurons) {
				TreePriv::free(n);
			}
		}
	}
};

Command* Command::changeType(SessionPriv* tp, Edge e, int16_t t) {
	return new CommandChangeType{tp, e, t};
}

Command* Command::changeMark(SessionPriv* tp, Edge e, size_t ep, int16_t m) {
	return new CommandChangeMark{tp, e, ep, m};
}

Command* Command::changeSize(SessionPriv* tp, Edge e, size_t ep, uint16_t m) {
	return new CommandChangeSize{tp, e, ep, m};
}

Command* Command::createNeuron(SessionPriv* tp, Vertex v) {
	return new CommandNewNeuron{tp, v};
}

Command* Command::renameNeuron(SessionPriv* tp, Tree n, const std::string& nm) {
	return new CommandRenameNeuron{tp, n, nm};
}

Command* Command::removeNeurons(SessionPriv* tp, const std::vector<size_t>& idxes) {
	return new CommandRemoveNeurons{tp, idxes};
}

Command* Command::purgeNeurons(SessionPriv* tp, const std::vector<size_t>& idxes) {
	return new CommandPurgeNeurons{tp, idxes};
}

Command* Command::finishBranch(SessionPriv* tp, Vertex v, Edge e, size_t ei) {
	return new CommandFinishBranch{tp, v, e, ei};
}

Command* Command::removeEdge(SessionPriv* tp, Edge e0, size_t ei0, Edge e1, size_t ei1) {
	return new CommandRemoveEdge{tp, e0, ei0, e1, ei1};
}

Command* Command::createBranch(SessionPriv* tp, const Path& path) {
	return new CommandCreateBranch{tp, path};
}

Command* Command::extendBranch(SessionPriv* tp, const Path& path) {
	return new CommandExtendBranch{tp, path};
}

Command* Command::importSwc(SessionPriv* tp, const std::vector<Point>& points, const std::vector<int64_t>& parents) {
	return new CommandImportSwc{tp, points, parents};
}
Command* Command::importFnt(SessionPriv* tp, const std::vector<bool>& vertInfo, const std::vector<std::tuple<size_t, size_t, int16_t, std::vector<Point>>>& edgInfo, const std::vector<std::tuple<size_t, std::string>>& somaInfo) {
	return new CommandImportFnt{tp, vertInfo, edgInfo, somaInfo};
}
